#include "Map.h"
#include <algorithm>
#include "GFuns.h"

Map::Map(Character* aHero, int aSeed)
{
	parentMap = nullptr;
	hero = aHero;
	seed = aSeed;
	weather = NULL;
}

Map::~Map()
{
	for(map<Node,Map*>::iterator it=childMaps.begin(); it!=childMaps.end(); it++)
		delete it->second;
	childMaps.clear();

	for(unsigned int c=0; c<characters.size(); c++)
		if(characters[c] != hero) delete characters[c];

	for(unsigned int p=0; p<effects.size(); p++)
		delete effects[p];

	if(weather) delete weather;
	weather = NULL;
}

bool CharacterComparator(Character* a, Character* b)
{
	if(a->GetPositionY()+a->GetOffsetY() < b->GetPositionY()+b->GetOffsetY()) return true;
	else return false;
}

void Map::SetHero(Character* aHero)
{
	hero = aHero;
}

void Map::Tick()
{
	occupiedTiles.clear();
	for(unsigned int i=0; i<characters.size(); i++)
	{
		Node n = {characters[i]->GetPositionX(), characters[i]->GetPositionY()};
		//if((abs(characters[i]->GetOffsetX()) < 0.5 && characters[i]->GetOffsetX() != 0) ||
		//   (abs(characters[i]->GetOffsetY()) < 0.5 && characters[i]->GetOffsetY() != 0) ||
		//	  characters[i]->GetOffsetX() == 0 && characters[i]->GetOffsetY() == 0)
			occupiedTiles.push_back(n);
	}

	for(unsigned int i=0; i<characters.size(); i++)
	{
		if(characters[i] != hero)
		{
			if(!characters[i]->IsMoving() && characters[i]->IsMobile())
			{
				int x = characters[i]->GetPositionX();
				int y = characters[i]->GetPositionY();

				int newDir = rand()%4;
				bool pass;
				if(newDir == RIGHT) pass = IsPassable(x+1,y);
				if(newDir == LEFT) pass = IsPassable(x-1,y);
				if(newDir == UP) pass = IsPassable(x,y-1);
				if(newDir == DOWN) pass = IsPassable(x,y+1);

				if(pass && GetTile(x,y,newDir).upper != O_WELL) characters[i]->Move(newDir);
				if(!pass) characters[i]->Stop();

				Node newOccupied = {characters[i]->GetPositionX(), characters[i]->GetPositionY()};
				occupiedTiles.push_back(newOccupied);
			}
			else if(!characters[i]->IsMobile())
			{
				characters[i]->SetFacing(hero->GetPositionX(),hero->GetPositionY());
			}
		}

		int lower = getLower(characters[i]->GetPositionX(),characters[i]->GetPositionY());
		int upper = getUpper(characters[i]->GetPositionX(),characters[i]->GetPositionY());
		if(_mapType == MAP_WORLD)
			if((lower == POLAR || lower == DESERT || lower == SAVANNA || lower == STEPPE) && upper != ROAD) characters[i]->SetTrace(true);
			else characters[i]->SetTrace(false);
		if(_mapType == MAP_CITY)
			if(lower == F_SOIL && upper == OBJECT_NONE && (_biome == POLAR || _biome == DESERT || _biome == SAVANNA || _biome == STEPPE))
				characters[i]->SetTrace(true);
			else characters[i]->SetTrace(false);

		characters[i]->Tick();
	}

	sort(characters.begin(),characters.end(),CharacterComparator);

	for(unsigned int p=0; p<effects.size(); p++)
		effects[p]->Tick();

	for(unsigned int l=0; l<lightSpots.size(); l++)
		if(rand()%2 == 0)
			lightSpots[l].Tick();

	if(weather) weather->Tick();
}

bool Map::setLower(int aX, int aY, int aLower){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return false;
	_TileMap(aX,aY).lower = aLower;
	_changed = true;
	return true;
}

bool Map::setUpper(int aX, int aY, int aUpper){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return false;
	_TileMap(aX,aY).upper  = aUpper;
	_changed = true;
	return true;
}

void Map::setSeed(int aX, int aY, int aV){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return;
	_TileMap(aX,aY).seed  = aV;
	_changed = true;
}

void Map::setSpawn(int aX, int aY, int aJob){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return;
	_TileMap(aX,aY).spawn  = aJob;
	_changed = true;
}

void Map::SetPassable(int aX, int aY, bool aPass){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return;
	_TileMap(aX,aY).passable = aPass;
	_changed = true;
}

int Map::getLower(int aX, int aY){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return NONE;
	return _TileMap(aX,aY).lower;
}

int Map::getUpper(int aX, int aY){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return NONE;
	return _TileMap(aX,aY).upper;
}

int Map::getSeed(int aX, int aY){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return NONE;
	return _TileMap(aX,aY).seed;
}

int Map::getSpawn(int aX, int aY){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return NONE;
	return _TileMap(aX,aY).spawn;
}

bool Map::IsPassable(int aX, int aY, bool ignoreCharacters){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0) return false;

	if(ignoreCharacters) return _TileMap(aX,aY).passable;

	if(!_TileMap(aX,aY).passable) return false;

	Node n = {aX,aY};
	for(unsigned int i=0; i<occupiedTiles.size(); i++)
		if(occupiedTiles[i] == n)
			return false;
	return true;
}

bool Map::IsValid(int aX, int aY)
{
	return !(aX < 0 || aY < 0 || aX >= _tilesX || aY >= _tilesY);
}

Tile Map::getTile(int aX, int aY){
	if(aX >= _tilesX || aX < 0
		|| aY >= _tilesY || aY < 0){
			Tile t;
			return t;
	}
	return _TileMap(aX,aY);
}

Tile Map::GetTile(int aX, int aY, int aDir){
	if(aDir == UP) aY--;
	else if(aDir == DOWN) aY++;
	else if(aDir == LEFT) aX--;
	else if(aDir == RIGHT) aX++;

	return getTile(aX,aY);
}

void Map::CreateCharacters()
{
	for(unsigned int i=0; i<characters.size(); i++) 
		if(characters[i] != hero) delete characters[i];
	characters.clear();

	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		if(getSpawn(x,y) != 0)
		{
			int s = getSpawn(x,y);
			Character* c = new Character(s,getBiome());
			c->SetPosition(x,y);
			if(s != JOB_KING && s!= JOB_SOLDIER && s!= JOB_BARTENDER  && s!= JOB_PRIEST) c->SetMobile(true);
			if(getLower(x,y) == F_STONE) c->SetMobile(false);
			if(!c->IsMobile()) SetPassable(x,y,false);
			characters.push_back(c);
		}
	}

	characters.push_back(hero);
	fprintf(stderr,"  - Characters created\n");
}

void Map::CreateTileActions()
{
	for(unsigned int i=0; i<tileActions.size(); i++) 
		delete tileActions[i];
	tileActions.clear();

	vector<Node> possibleTileActions;
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		EObject o = (EObject)getUpper(x,y);
		if(o == O_CHEST || o == O_BARREL || o == O_CRATE || o == O_WARDROBE || o == O_GRAVE || o == O_MARKETSTAND)
		{
			Node n = {x,y};
			possibleTileActions.push_back(n);
		}
	}

	if(_mapType == MAP_MAZE)
	{
		int rndInt = rand()%possibleTileActions.size();
		Node rnd = possibleTileActions[rndInt];
		Item silverKettle(ITEM_QUEST, QUEST_SILVER_KETTLE);
		TileAction* ta = new TileAction(getTile(rnd.x,rnd.y),rnd.x,rnd.y, &silverKettle);
		tileActions.push_back(ta);
		possibleTileActions.erase(possibleTileActions.begin()+rndInt);
		printf("x: %d, y: %d\n",rnd.x,rnd.y);
	}

	for(unsigned int i=0; i<possibleTileActions.size(); i++)
	{
		Node n = possibleTileActions[i];
		TileAction* t = new TileAction(getTile(n.x,n.y),n.x,n.y);
		tileActions.push_back(t);
	}

	fprintf(stderr,"  - Tile actions created\n");
}

Character* Map::GetRandomCharacter(EJob aJob)
{
	vector<Character*> chars;
	for(unsigned int i=0; i<characters.size(); i++)
		if(characters[i]->GetProfession() == aJob)
			chars.push_back(characters[i]);

	if(chars.size() == 0) return nullptr;
	return chars[rand()%chars.size()];
}

void Map::ApplyQuest(Quest* aQuest, Character* aCharacter)
{
	aCharacter->quests.push_back(aQuest);
	hero->quests.push_back(aQuest);
	aQuest->owner = aCharacter;
}

void Map::CreateQuests()
{
	Character* c;
	Character* c2;

	c = GetRandomCharacter(JOB_PRIEST);
	if(c) ApplyQuest(GetQuestGodsTears(),c);

	c = GetRandomCharacter(JOB_SOLDIER);
	c2 = GetRandomCharacter(JOB_ARMORSMITH);
	if(c && c2) ApplyQuest(GetQuestCaravan(c2),c);

	c = GetRandomCharacter(JOB_CHEF);
	if(c) ApplyQuest(GetQuestSilverKettle(),c);
}

Character* Map::GetCharacterAt(int aX, int aY)
{
	for(unsigned int i=0; i<characters.size(); i++)
	{
		if(characters[i]->GetPositionX() == aX && characters[i]->GetPositionY() == aY)
			return characters[i];
	}
	return nullptr;
}

Character* Map::GetHeroInterlocutor()
{
	Character* character = nullptr;
	if(hero->GetFacing() == UP   ) character = GetCharacterAt(hero->GetPositionX()+0, hero->GetPositionY()-1);
	if(hero->GetFacing() == DOWN ) character = GetCharacterAt(hero->GetPositionX()+0, hero->GetPositionY()+1);
	if(hero->GetFacing() == LEFT ) character = GetCharacterAt(hero->GetPositionX()-1, hero->GetPositionY()+0);
	if(hero->GetFacing() == RIGHT) character = GetCharacterAt(hero->GetPositionX()+1, hero->GetPositionY()+0);

	return character; 
}

TileAction* Map::GetTileActionAt(int aX, int aY)
{
	for(unsigned int i=0; i<tileActions.size(); i++)
	{
		if(tileActions[i]->x == aX && tileActions[i]->y == aY)
			return tileActions[i];
	}
	return nullptr;
}

TileAction* Map::GetHeroTileAction()
{
	TileAction* tileAction = nullptr;
	if(hero->GetFacing() == UP   ) tileAction = GetTileActionAt(hero->GetPositionX()+0, hero->GetPositionY()-1);
	if(hero->GetFacing() == DOWN ) tileAction = GetTileActionAt(hero->GetPositionX()+0, hero->GetPositionY()+1);
	if(hero->GetFacing() == LEFT ) tileAction = GetTileActionAt(hero->GetPositionX()-1, hero->GetPositionY()+0);
	if(hero->GetFacing() == RIGHT) tileAction = GetTileActionAt(hero->GetPositionX()+1, hero->GetPositionY()+0);

	return tileAction; 
}

void Map::Generate(int aTilesX, int aTilesY)
{
	_tilesX = aTilesX;
	_tilesY = aTilesY;
	Tile t;
	_TileMap.build(_tilesX,_tilesY,t);

	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		setLower(x,y,0);
		setUpper(x,y,0);
		setSpawn(x,y,0);
		SetPassable(x,y,0);
	}

	_changed = true;

	srand(seed);
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		int rnd;
		do{rnd = rand();} while(rnd==0);
		setSeed(x,y,rand());
	}

	GenerateMapData();
	CreateCharacters();
	if(_mapType != MAP_WORLD) CreateTileActions();
	CreateQuests();
}

/*void Map::save(){
	char* path = al_get_current_directory();
	ALLEGRO_FILECHOOSER* save = al_create_native_file_dialog(path,"Save current map...","*.*",ALLEGRO_FILECHOOSER_SAVE);
	if(al_show_native_file_dialog(al_get_current_display(),save)){
		fprintf(stderr,"> Saving file: %s\n",al_get_native_file_dialog_path(save,0));

		ofstream file;
		file.open(al_get_native_file_dialog_path(save,0),fstream::trunc);
		file<<_tilesX<<" "<<_tilesY<<"\n";
		for(int y=0; y<_tilesY; y++)
			for(int x=0; x<_tilesX; x++)
				file<<getLower(x,y)<<" "<<getUpper(x,y)<<" "<<getSeed(x,y)<<"\n";
		file<<"\n";
		file.close();
	}
	al_destroy_native_file_dialog(save);
}

void Map::load(){
	char* path = al_get_current_directory();
	ALLEGRO_FILECHOOSER* load = al_create_native_file_dialog(path,"Load map...","*.*",ALLEGRO_FILECHOOSER_FILE_MUST_EXIST);
	if(al_show_native_file_dialog(al_get_current_display(),load)){
		fprintf(stderr,"> Loading file: %s\n",al_get_native_file_dialog_path(load,0));

		ifstream file;
		file.open(al_get_native_file_dialog_path(load,0));
		file>>_tilesX;
		file>>_tilesY;

		for(int y=0; y<_tilesY; y++)
			for(int x=0; x<_tilesX; x++){
				int t;
				file>>t;
				setLower(x,y,t);
				file>>t;
				setUpper(x,y,t);
				file>>t;
				setSeed(x,y,t);
			}
		file.close();
	}
	al_destroy_native_file_dialog(load);
}*/